#include "head.h"

#define SIZE(a) (sizeof(a) / sizeof(a[0]))

int putsFile(int peerfd, char *para);
int getsFile(int peerfd, char *para);
void sendMsg(int peerfd, char *msg);
void sendUserPasswd(int peerfd);
void sendArray(int peerfd, char *msg,int size);

typedef enum
{
	CMD_TYPE_CD = 1,
	CMD_TYPE_LS = 2,
	CMD_TYPE_PWD = 4,
	CMD_TYPE_PUT = 8,
	CMD_TYPE_GET = 16,
	CMD_TYPE_RM = 32,
	CMD_TYPE_MKDIR = 64
} CmdType;

typedef struct
{
	int length;
	char buff[1024];
} train_t;

int recvCircle(int fd, char *buff, int length)
{
	char *pbuf = buff;
	int left = length;
	int ret = 0;
	while (left > 0)
	{
		ret = recv(fd, pbuf, left, 0);
		if (0 == ret)
		{
			close(fd);
			break;
		}
		else if (ret == -1 && errno == EINTR)
		{
			continue;
		}
		else if (ret == -1)
		{
			perror("recv");
			break;
		}
		else
		{ // > 0
			pbuf += ret;
			left -= ret;
		}
		// printf("recv %f%%\r",(float)ret/left * 100);
	}
	return length - left;
}

int main(int argc, char *argv[])
{
	// 0. 创建套接字
	int clientfd = socket(AF_INET, SOCK_STREAM, 0);
	ERROR_CHECK(clientfd, -1, "socket");

	printf("sizeof(struct sockaddr):%ld\n", sizeof(struct sockaddr));
	printf("sizeof(struct sockaddr_in):%ld\n", sizeof(struct sockaddr_in));

	// 1. 设置服务器的网络地址
	struct sockaddr_in serverAddr;
	memset(&serverAddr, 0, sizeof(serverAddr));

	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(8888);
	serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int ret = connect(clientfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
	ERROR_CHECK(ret, -1, "connect");

	sendUserPasswd(clientfd);

	printf("conn has connected\n");
	while (getchar() != '\n')//读取一行剩余的字符（丢掉）（包括换行符）
		;
	while (1)
	{
		printf("请输入指令:");
		fflush(stdout);

		char cmd[100] = {0};
		int i = 0;
		char ch = -1;
		while ((ch = getchar()) != '\n')
		{
			cmd[i++] = ch;
		}

		char Cmd[10] = {0};
		int len = 0;
		while (cmd[len] && cmd[len] != ' ')
		{
			len++;
		}
		strncpy(Cmd, cmd, len);
		char para[100] = {0};
		strncpy(para, cmd + len + 1, 100 - len);

		//先发四字节指令类型，再发100字节参数
		if (strcmp(Cmd, "ls") == 0)
		{
			int cmd = CMD_TYPE_LS;
			send(clientfd, &cmd, sizeof(cmd), MSG_WAITALL);
		}
		else if (strcmp(Cmd, "cd") == 0)
		{
			int cmd = CMD_TYPE_CD;
			send(clientfd, &cmd, sizeof(cmd), MSG_WAITALL);
		}
		else if (strcmp(Cmd, "pwd") == 0)
		{
			int cmd = CMD_TYPE_PWD;
			send(clientfd, &cmd, sizeof(cmd), MSG_WAITALL);
		}
		else if (strcmp(Cmd, "puts") == 0)
		{
			//上传下载文件需要执行对应函数进行
			int cmd = CMD_TYPE_PUT;
			send(clientfd, &cmd, sizeof(cmd), MSG_WAITALL);
			int len = putsFile(clientfd, para);
			//判断返回值，文件是否发送完成
			if (len != 0)
			{
				printf("puts error\n");
			}
			continue;
		}
		else if (strcmp(Cmd, "gets") == 0)
		{
			int cmd = CMD_TYPE_GET;
			send(clientfd, &cmd, sizeof(cmd), MSG_WAITALL);
			int len = getsFile(clientfd, para);
			if (len != 0)
			{
				printf("gets file error\n");
			}
			continue;
		}
		else if (strcmp(Cmd, "rm") == 0)
		{
			int cmd = CMD_TYPE_RM;
			send(clientfd, &cmd, sizeof(cmd), MSG_WAITALL);
		}
		else if (strcmp(Cmd, "mkdir") == 0)
		{
			int cmd = CMD_TYPE_MKDIR;
			send(clientfd, &cmd, sizeof(cmd), MSG_WAITALL);
		}
		else
		{
			printf("error cmd\n");
			continue;
		}
		sendMsg(clientfd,para);
		//send(clientfd, para, 100, MSG_WAITALL);

		//展示结果
		char recvbuf[4096];
		memset(&recvbuf, 0, 4096);
		printf("开始接收结果\n");
		int recvlen = 0;
		recvCircle(clientfd, (char *)&recvlen, 4);
		// recv(clientfd,recvbuf,100,0);
		printf("recvlen = %d\n",recvlen);
		recvCircle(clientfd, recvbuf, recvlen);
		puts(recvbuf);
	}
	close(clientfd);
}

int putsFile(int peerfd, char *para)
{
	// 1. 读取本地文件
	int fd = open(para, O_RDWR);
	ERROR_CHECK(fd, -1, "open");

	// 2. 先发文件名
	sendMsg(peerfd,para);

	// 3. 再发文件内容
	// 3.1 获取文件的长度
	struct stat st;
	fstat(fd, &st);
	printf("file's length: %ld\n", st.st_size);
	// printf("sizeof(st.st_size): %ld\n", sizeof(st.st_size));
	send(peerfd, &st.st_size, 8, MSG_WAITALL);

	//3.2收服务器端文件长度
	size_t sendSize = 0;
	recvCircle(peerfd,(char*)&sendSize,8);
	
	//3.3断点续传
	lseek(fd,sendSize,SEEK_SET);
	train_t train;
	while (sendSize < st.st_size)
	{
		memset(&train, 0, sizeof(train));
		//从本地文件中读取,将其放入train
		int ret = read(fd, train.buff, sizeof(train.buff));
		train.length = ret;
		//发送数据给到对端
		ret = send(peerfd, &train, 4 + train.length, MSG_WAITALL);
		if (ret > 0)
		{
			sendSize += (ret - 4);
		}
		else
		{
			perror("send");
			break;
		}
		//先传一部分，构造一个断点续传情况
		//sleep(10);
	}
	close(fd);
	return sendSize - st.st_size;
}

int getsFile(int peerfd, char *para)
{
	// 1. 先发文件名，之后可以发md5值。
	train_t train;
	memset(&train, 0, sizeof(train));
	train.length = strlen(para); // 100
	strcpy(train.buff, para);
	int ret = send(peerfd, &train, 4 + train.length, MSG_WAITALL); // 104

	// 2.在本地建立文件
	int fd = open(para, O_RDWR | O_CREAT | O_TRUNC, 0664);
	ERROR_CHECK(fd, -1, "open");

	// 3. 再接收文件内容
	// 3.1 先接文件长度
	size_t filelength = 0;

	recvCircle(peerfd, (char *)&filelength, 8);
	printf("filecontent length: %ld\n", filelength);

	// 3.2 再接文件内容
	int length = 0;
	size_t recvSize = 0;
	while (recvSize < filelength)
	{
		char buff[1024] = {0};
		recvCircle(peerfd, (char *)&length, 4);
		recvCircle(peerfd, buff, length);
		// 3. 写入本地
		write(fd, buff, length);
		recvSize += length;
	}
	close(fd);
	return recvSize - filelength;
}

void sendMsg(int peerfd, char *msg)
{
	train_t train;
	train.length = strlen(msg);
	strcpy(train.buff, msg);
	send(peerfd, &train, train.length + 4, MSG_WAITALL);
}
void sendArray(int peerfd, char *msg,int size)
{
	train_t train;
	train.length = size;
	strcpy(train.buff, msg);
	send(peerfd, &train, train.length + 4, MSG_WAITALL);
}

void sendUserPasswd(int peerfd)
{
	while (1)
	{
		// 1.发送用户名
		puts("请输入用户名");
		char username[50] = {0};
		scanf("%s", username);
		sendArray(peerfd, username,strlen(username));

		//1.2验证用户名是否拥有
		char flag = -1;
		recv(peerfd,&flag,1,0);
		if (flag == 0)
		{
			printf("username error!\n");
			continue;
		}

		// 2.接收盐值
		int lenOfpass = 0;
		char salt[50] = {0};
		recvCircle(peerfd, (char *)&lenOfpass, 4);
		recvCircle(peerfd, salt, lenOfpass);

		printf("salt = %s\n",salt);
		// 3.输入密码，计算密文并传输
		puts("请输入密码");
		char passwd[100] = {0};
		scanf("%s", passwd);
		char *cryptograph = crypt(passwd, salt);
		printf("cryptograph = %s\n",cryptograph);
		sendMsg(peerfd, cryptograph);

		// 4.接收返回结果是否登陆成功
		recv(peerfd, &flag, 1, MSG_WAITALL);
		printf("flag = %d\n",flag);
		if (flag == 1)
		{
			printf("login success!\n");
			break;
		}
		else
		{
			printf("password error!\n");
		}
	}
}